#!/usr/bin/env python2

import os
import sys
import socket
import time
import subprocess
import fcntl
import types
import errno
import getopt

from config import Config
from instence import Instence
from storage import Storage
from utils import Exp, _dmsg, _dwarn, _derror, _get_value, mutil_exec, _exec_pipe, _str2dict
from cluster_scan import ClusterScan
from metanode_balance_with_diskmap import balance as metanode_balance
from metanode_balance_with_diskmap import get_lst, dropmeta, setmeta
#import task

recover_thread = 30

class InstenceScan:
    def __init__(self, instence):
        self.config = instence.config
        self.storage = Storage(self.config)
        self.instence = instence
    
#     @task.rtk()
    def metabalance(self):
        if (self.instence.isadmin()):
            _dmsg("scan meta node")
            clusterscan = ClusterScan(self.instence.home)
            clusterscan.scan()

#     @task.rtk()
    def _recover(self, is_recover = False, verbose = False):
        if (self.instence.isadmin() and is_recover):
            _dmsg("recover meta node")
            lst = get_lst()

            metanode_balance(lst)

            metas = []
            #need exec with the admin instence
            res = _exec_pipe([self.instence.lich_admin, '--stat', self.instence.name], 3, False)[:-1]
            if (len(res) != 0):
                d = _str2dict(res)
                metas = d['metas'].strip(",").split(",")

            _dmsg("drop metas that was stopped: %s, lst; %s" % (str(metas), str(lst)))
            for m in metas:
                if (m not in lst.keys()):
                    dropmeta(m)
                else:
                    v = lst[m]
                    if ( v == 'stopped' or 'deleting' in v):
                        dropmeta(m)

        heads = []
        heads = self.__build_list(heads, self.instence.home + "/chunk/meta")
        heads = heads + self.__build_list(heads, self.instence.home + "/chunk/node")
        heads = list(set(heads))

        idx = 0
        while (1):
            lst = heads[idx : idx + recover_thread]
            if (len(lst) == 0):
                break

            s = ''
            for i in lst:
                s += (i + ',')

            if (s[-1] == ','):
                arg = s[:-1]
            else:
                arg = s

            if (verbose):
                print ('scan: ' + arg)

            try:
                self.storage.recover(arg, is_recover, verbose)
            except Exp, e:
                _derror("%s" % (e.err))
                return e.errno
                
            idx += recover_thread

    def scan(self, verbose = False):
        return self._recover(is_recover = False, verbose = False)

    def recover(self, verbose = False):
        return self._recover(is_recover = True, verbose = False)
        
    def recover_replica(self):
        _dmsg("recover_replica node")
        heads = []
        heads = self.__build_list(heads, self.instence.home + "/chunk/meta")
        heads = heads + self.__build_list(heads, self.instence.home + "/chunk/node")
        heads = list(set(heads))

        idx = 0
        while (1):
            lst = heads[idx : idx + recover_thread]
            if (len(lst) == 0):
                break

            s = ''
            for i in lst:
                s += (i + ',')

            if (s[-1] == ','):
                arg = s[:-1]
            else:
                arg = s

            print ('recover_replica: ' + arg)

            try:
                self.storage.recover_replica(arg)
            except Exp, e:
                _derror("%s" % (e.err))
                return e.errno

            idx += recover_thread

    def __build_list(self, head, path):
        try:
            ls = os.listdir(path)
        except OSError as err:
            if err.errno != errno.ENOENT:
                raise
            else:
                return head
            
        for i in ls:
            s = i.split('.')[-1]
            if (s in ['attr', 'info', 'version']):
                continue
            new = os.path.join(path, i)
            if (os.path.isdir(new)):
                self.__build_list(head, new)
            else:
                head.append(new)
        return head

def usage():
    print ("usage:")
    print (sys.argv[0] + " --balance <path>")
    print (sys.argv[0] + " --recover <path>")
    #print (sys.argv[0])

def main():
    verbose = 0

    try:
        opts, args = getopt.getopt(
                sys.argv[1:], 
                'hv', ['balance=', 'help', 'recover=', 'verbose']
                )
    except getopt.GetoptError, err:
        print str(err)
        usage()

    if (len(sys.argv) == 1):
        usage()
        exit(0)

    config = Config()
    for o, a in opts:
        if o in ('--help'):
            usage()
            exit(0)
        elif o in ('-v', '--verbose'):
            verbose = 1
        elif o == '--balance':
            instence = Instence(config, a)
            instence_scan = InstenceScan(instence)
            instence_scan.balance()
        elif o == '--recover':
            instence = Instence(config, a)
            instence_scan = InstenceScan(instence)
            instence_scan.recover(verbose)
        else:
            assert False, 'oops, unhandled option: %s, -h for help' % o
            exit(1)

if __name__ == '__main__':
    main()
